using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Ink;

namespace WozControls
{
    public partial class PenCanvas : UserControl
    {
        public enum PenCanvasMode { SelectEntities, DrawStrokes };

        public bool DrawingEnabled
        {
            get
            {
                return this.mInkCollector.Enabled;
            }

            set
            {
                this.mInkCollector.Enabled = value;
            }
        }

        public Microsoft.Ink.Ink Ink
        {
            get
            {
                return this.mInkCollector.Ink;
            }
        }

        public System.Drawing.Graphics Graphics
        {
            get
            {
                return this.mGraphics;
            }
        }

        public Strokes SelectedStrokes
        {
            get
            {
                return this.mSelectedStrokes;
            }
        }

        public PenCanvas()
        {
            // initialize the user interface components
            InitializeComponent();

            // initialize the ink
            this.InitializeInk();

            // create the various modes
            this.CreateModes();

            // set the current mode
            this.CurrentMode = PenCanvasMode.DrawStrokes;

            // create the graphics
            this.mGraphics = this.CreateGraphics();

            // instantiate the selected strokes
            this.mSelectedStrokes = this.mInkCollector.Ink.CreateStrokes();

            this.DoubleBuffered = true;
        }

        ~PenCanvas()
        {
            // kill the graphics we created
            this.mGraphics.Dispose();
        }

        private void CreateModes()
        {
            // instantiate the modes
            this.mModes = new SortedDictionary<PenCanvasMode, PenCanvasModeBase>();

            // fill them in
            this.mModes[PenCanvasMode.SelectEntities] = new SelectEntitiesMode(this);
            this.mModes[PenCanvasMode.DrawStrokes] = new DrawStrokesMode(this);
        }

        public PenCanvasMode CurrentMode
        {
            /*get
            {
                return this.mCurrentMode;
            }*/

            set
            {
                // tell the current mode to close down
                if (this.mCurrentMode != null)
                {
                    this.mCurrentMode.UnSetAsCurrent();
                }

                // update the current mode variable
                this.mCurrentMode = this.mModes[value];

                // tell the new current mode to turn on
                this.mCurrentMode.SetAsCurrent();
            }
        }

        public void PixelToInkSpace(ref Point pPoint)
        {
            this.mInkCollector.Renderer.PixelToInkSpace(this.Handle, ref pPoint);
        }

        public void SelectStroke(Stroke pStrokeToSelect)
        {
            // first color the stroke red
            pStrokeToSelect.DrawingAttributes.Color = Color.Red;

            // add this stroke to the list of selected strokes
            this.mSelectedStrokes.Add(pStrokeToSelect);

            // refresh ourselves to show the change
            //this.Refresh();
            this.Invalidate();

            // fire the event
            if (this.StrokesSelected != null)
            {
                this.StrokesSelected(this, new EventArgs());
            }
        }

        public void SelectStrokes(Strokes pStrokesToSelect)
        {
            foreach (Stroke stroke in pStrokesToSelect)
            {
                // first color the stroke red
                stroke.DrawingAttributes.Color = Color.Red;

                // add this stroke to the list of selected strokes
                this.mSelectedStrokes.Add(stroke);
            }

            // refresh ourselves to show the change
            this.Refresh();

            // fire the event
            if (this.StrokesSelected != null)
            {
                this.StrokesSelected(this, new EventArgs());
            }
        }

        public void DeselectStroke(Stroke pStrokeToDeselect)
        {
            // first make sure this is actually in our list of selected
            if (this.mSelectedStrokes.Contains(pStrokeToDeselect))
            {
                // it is in our list so lets color it black
                pStrokeToDeselect.DrawingAttributes.Color = Color.Black;

                // and take it out of the list
                this.mSelectedStrokes.Remove(pStrokeToDeselect);
            }

            // refresh ourselves to show the change
            this.Refresh();

            // fire the event
            if (this.StrokesDeselected != null)
            {
                this.StrokesDeselected(this, new EventArgs());
            }
        }

        public void DeselectStrokes(Strokes pStrokesToDeselect)
        {
            foreach (Stroke stroke in pStrokesToDeselect)
            {
                // first make sure this is actually in our list of selected
                if (this.mSelectedStrokes.Contains(stroke))
                {
                    // it is in our list so lets color it black
                    stroke.DrawingAttributes.Color = Color.Black;

                    // and take it out of the list
                    this.mSelectedStrokes.Remove(stroke);
                }
            }

            // refresh ourselves to show the change
            this.Refresh();

            // fire the event
            if (this.StrokesDeselected != null)
            {
                this.StrokesDeselected(this, new EventArgs());
            }
        }

        public void DeselectAllStrokes()
        {
            // loop through and bring all their colors to black
            foreach (Stroke stroke in this.mSelectedStrokes)
            {
                if (stroke.DrawingAttributes != null)
                {
                    stroke.DrawingAttributes.Color = Color.Black;
                }
            }

            // remove them from our list of selected strokes
            this.mSelectedStrokes.Clear();
            
            // refresh ourselves to show the change
            this.Refresh();

            // fire the event
            if (this.StrokesDeselected != null)
            {
                this.StrokesDeselected(this, new EventArgs());
            }
        }

        public void CutSelectedStrokes()
        {
            // first make the selected strokes colored black
            foreach (Stroke stroke in this.mSelectedStrokes)
            {
                stroke.DrawingAttributes.Color = Color.Black;
            }

            // tell the ink collector to copy the selected strokes
            this.mInkCollector.Ink.ClipboardCopy(this.mSelectedStrokes,
                InkClipboardFormats.InkSerializedFormat,
                InkClipboardModes.Cut);

            // deselect whatever was selected
            this.DeselectAllStrokes();

            // first the event
            if (this.StrokesCut != null)
            {
                this.StrokesCut(this, new EventArgs());
            }
        }

        public void CopySelectedStrokes()
        {
            // tell the ink collector to copy the selected strokes
            this.mInkCollector.Ink.ClipboardCopy(this.mSelectedStrokes,
                InkClipboardFormats.InkSerializedFormat,
                InkClipboardModes.Copy);

            // first the event
            if (this.StrokesCopied != null)
            {
                this.StrokesCopied(this, new EventArgs());
            }
        }

        public void PasteCopiedStrokes()
        {
            // first deselect whatever strokes may be currently selected
            this.DeselectAllStrokes();

            // tell the ink collector to paste the strokes
            Strokes pastedStrokes = this.mInkCollector.Ink.ClipboardPaste();

            // select whatever was just pasted
            this.SelectStrokes(pastedStrokes);

            // first the event
            if (this.StrokesPasted != null)
            {
                this.StrokesPasted(this, new EventArgs());
            }
        }

        public void CloneSelectedStrokes()
        {
            // tell the ink collector to copy the selected strokes
            this.mInkCollector.Ink.ClipboardCopy(this.mSelectedStrokes,
                InkClipboardFormats.InkSerializedFormat,
                InkClipboardModes.Copy);

            // deselect whatever strokes may be currently selected
            this.DeselectAllStrokes();

            // tell the ink collector to paste the strokes
            Strokes pastedStrokes = this.mInkCollector.Ink.ClipboardPaste();

            // select whatever was just pasted
            this.SelectStrokes(pastedStrokes);

            // first the event
            if (this.StrokesCloned != null)
            {
                this.StrokesCloned(this, new EventArgs());
            }
        }

        public void DeleteSelectedStrokes()
        {
            // first erase them
            this.Ink.DeleteStrokes(this.mSelectedStrokes);
            
            // then deselect all these strokes
            this.DeselectAllStrokes();
            
            // first the event
            if (this.StrokesDeleted != null)
            {
                this.StrokesDeleted(this, new EventArgs());
            }
        }

        private Strokes mSelectedStrokes;

        public event EventHandler StrokesSelected;
        public event EventHandler StrokesDeselected;
        public event EventHandler StrokesCut;
        public event EventHandler StrokesCopied;
        public event EventHandler StrokesPasted;
        public event EventHandler StrokesCloned;
        public event EventHandler StrokesDeleted;

        private bool InitializeInk()
        {
            // instantiate the ink collector
            this.mInkCollector = new InkCollector(this.Handle);

            // set the pen width
            this.mInkCollector.DefaultDrawingAttributes.Width = 125.0f;

            // enable the pen
            this.mInkCollector.Enabled = true;

            return true;
        }

        

        private InkCollector mInkCollector;

        private PenCanvasModeBase mCurrentMode;

        private SortedDictionary<PenCanvasMode, PenCanvasModeBase> mModes;

        private Graphics mGraphics;

        private void PenCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            // tell the current mode to handle this message
            this.mCurrentMode.MouseDown(e);
        }

        private void PenCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            // tell the current mode to handle this message
            this.mCurrentMode.MouseMove(e);
        }

        private void PenCanvas_MouseUp(object sender, MouseEventArgs e)
        {
            // tell the current mode to handle this message
            this.mCurrentMode.MouseUp(e);
        }

        private void PenCanvas_Paint(object sender, PaintEventArgs e)
        {
            
        }

        private void PenCanvas_Resize(object sender, EventArgs e)
        {
            // we have been resized so recreate the graphics
            this.mGraphics.Dispose();
            this.mGraphics = this.CreateGraphics();
        }

        
    }




















    abstract class PenCanvasModeBase
    {
        public PenCanvasModeBase()
        {
        }

        public abstract void SetAsCurrent();
        public abstract void UnSetAsCurrent();

        public abstract void MouseDown(MouseEventArgs pEventArgs);
        public abstract void MouseMove(MouseEventArgs pEventArgs);
        public abstract void MouseUp(MouseEventArgs pEventArgs);

        protected PenCanvas mPenCanvas;
    }

    class SelectEntitiesMode : PenCanvasModeBase
    {
        public SelectEntitiesMode(PenCanvas pPenCanvas)
        {
            // copy over the passed in pen canvas to our local variable
            this.mPenCanvas = pPenCanvas;

            // initialize the selecting flags
            this.mMouseDown = false;
            this.mDragging = false;

            // instantiate the selection rectangle
            this.mSelectionRectangle = new Rectangle();
            
            // create the pen and brush for the selection rectangle
            this.mSelectionRectanglePen = new Pen(Color.DarkBlue, 2.0f);
            this.mSelectionRectangleBrush = new SolidBrush(Color.LightBlue);
        }

        ~SelectEntitiesMode()
        {
            // kill the pen and brush we created
            this.mSelectionRectanglePen.Dispose();
            this.mSelectionRectangleBrush.Dispose();
        }

        public override void SetAsCurrent()
        {
            // tell the pen canvas to stop drawing
            this.mPenCanvas.DrawingEnabled = false;
        }

        public override void UnSetAsCurrent()
        {
            
        }

        public override void MouseDown(MouseEventArgs pEventArgs)
        {
            // save the mouse down position
            this.mSelectionRectangle.Location = this.mMouseDownPosition = pEventArgs.Location;

            // set the size of the selection rectangle
            //this.mSelectionRectangle.Size = new Size(1, 1);

            // turn the selecting flag on
            this.mMouseDown = true;
        }

        public override void MouseMove(MouseEventArgs pEventArgs)
        {
            // expand the rectangle if were selecting
            if (this.mMouseDown)
            {
                // turn the dragging flag on
                this.mDragging = true;

                /*// invalidate the previous rectangle
                //this.mPenCanvas.Invalidate(this.mSelectionRectangle);
                
                this.mPenCanvas.Refresh();

                this.mSelectionRectangle.Width = pEventArgs.X - this.mMouseDownPosition.X;
                this.mSelectionRectangle.Height = pEventArgs.Y - this.mMouseDownPosition.Y;

                if (this.mSelectionRectangle.Width < 0)
                {
                    
                    this.mSelectionRectangle.X = pEventArgs.X;
                    this.mSelectionRectangle.Width *= -1;
                }

                if (this.mSelectionRectangle.Height < 0)
                {
                    this.mSelectionRectangle.Y = pEventArgs.Y;
                    this.mSelectionRectangle.Height *= -1;
                }

                //System.Drawing.Rectangle r = new Rectangle(50, 50, 300, 300);
                this.mGraphics.DrawRectangle(this.mSelectionRectanglePen, this.mSelectionRectangle);
                //this.mGraphics.FillRectangle(this.mSelectionRectangleBrush, this.mSelectionRectangle);

                //this.mPenCanvas.Refresh();
                //myPen.Dispose();
                //formGraphics.Dispose();*/
                
            }
        }

        public override void MouseUp(MouseEventArgs pEventArgs)
        {
            // well use this variable to fill up the strokes that were intersected with the hit test
            Strokes intersectedStrokes;

            // handle this in two different ways
            // a rectangle hit test if we were dragging and a point hit test if we werent dragging
            if (this.mDragging)
            {
                // TODO: do the hit test for the rectangle
                //intersectedStrokes = null;

                // convert this point to ink space
                this.mPenCanvas.PixelToInkSpace(ref this.mMouseDownPosition);

                // perform the hit test
                intersectedStrokes = this.mPenCanvas.Ink.HitTest(this.mMouseDownPosition, 10.0f);

                // turn the dragging flag off
                this.mDragging = false;
            }
            else
            {
                // convert this point to ink space
                this.mPenCanvas.PixelToInkSpace(ref this.mMouseDownPosition);

                // perform the hit test
                intersectedStrokes = this.mPenCanvas.Ink.HitTest(this.mMouseDownPosition, 10.0f);
            }

            

            // we did the hit test now see if it actually caught anything
            if (intersectedStrokes.Count >= 1)
            {
                // if shift is not down
                {
                    // first deselect any selected strokes
                    this.mPenCanvas.DeselectAllStrokes();
                }

                // tell the canvas to select this stroke
                this.mPenCanvas.SelectStroke(intersectedStrokes[0]);

                //foreach (Stroke stroke in intersectedStrokes)
                //{
                //    stroke.DrawingAttributes.Color = Color.Red;
                //}

                // tell the ink object to delete whatever strokes were intersected
                //this.mPenCanvas.Ink.DeleteStrokes(intersectedStrokes);
            }

            else
            {
                // or if there was a click and nothing was selected then deselect as well
                
                // but first check to see that something is actually selected
                if (this.mPenCanvas.SelectedStrokes.Count > 0)
                {
                    // there are things selected so deselect them
                    this.mPenCanvas.DeselectAllStrokes();
                }
            }

            // turn the selecting flag off
            this.mMouseDown = false;
        }

        private System.Drawing.Rectangle mSelectionRectangle;

        private bool mMouseDown;
        private bool mDragging;
        private Point mMouseDownPosition;
        private Pen mSelectionRectanglePen;
        private SolidBrush mSelectionRectangleBrush;
    }

    class DrawStrokesMode : PenCanvasModeBase
    {
        public DrawStrokesMode(PenCanvas pPenCanvas)
        {
            // copy over the passed in pen canvas to our local variable
            this.mPenCanvas = pPenCanvas;
        }

        public override void SetAsCurrent()
        {
            // tell the pen canvas to start drawing
            this.mPenCanvas.DrawingEnabled = true;
        }

        public override void UnSetAsCurrent()
        {
            
        }

        public override void MouseDown(MouseEventArgs pEventArgs)
        {
            // the select mode will turn the background green
            //this.mPenCanvas.BackColor = Color.Red;
        }

        public override void MouseMove(MouseEventArgs pEventArgs)
        {
        }

        public override void MouseUp(MouseEventArgs pEventArgs)
        {
        }
    }


}